home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / orca / braille.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  38.2 KB  |  1,208 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. '''A very experimental approach to the refreshable Braille display.  This
  5. module treats each line of the display as a sequential set of regions, where
  6. each region can potentially backed by an Accessible object.  Depending upon
  7. the Accessible object, the cursor routing keys can be used to perform
  8. operations on the Accessible object, such as invoking default actions or
  9. moving the text caret.
  10. '''
  11. __id__ = '$Id: braille.py 4311 2008-10-30 12:50:03Z wwalker $'
  12. __version__ = '$Revision: 4311 $'
  13. __date__ = '$Date: 2008-10-30 08:50:03 -0400 (Thu, 30 Oct 2008) $'
  14. __copyright__ = 'Copyright (c) 2005-2008 Sun Microsystems Inc.'
  15. __license__ = 'LGPL'
  16. import logging
  17. log = logging.getLogger('braille')
  18. import signal
  19.  
  20. try:
  21.     import louis
  22. except ImportError:
  23.     louis = None
  24.     _defaultContractionTable = None
  25.  
  26. _defaultContractionTable = louis.getDefaultTable()
  27.  
  28. try:
  29.     import brlapi
  30.     import gobject
  31.     brlAPI = None
  32.     useBrlAPIBindings = True
  33.     brlAPIRunning = False
  34.     brlAPISourceId = 0
  35. except:
  36.     import brl
  37.     useBrlAPIBindings = False
  38.     brlAPIRunning = False
  39.  
  40.  
  41. try:
  42.     import brlmon
  43. except:
  44.     pass
  45.  
  46. import debug
  47. import eventsynthesizer
  48. import orca_state
  49. import settings
  50. from orca_i18n import _
  51. _initialized = False
  52. monitor = None
  53. CMD_NOOP = 0
  54. CMD_LNUP = 1
  55. CMD_LNDN = 2
  56. CMD_WINUP = 3
  57. CMD_WINDN = 4
  58. CMD_PRDIFLN = 5
  59. CMD_NXDIFLN = 6
  60. CMD_ATTRUP = 7
  61. CMD_ATTRDN = 8
  62. CMD_TOP = 9
  63. CMD_BOT = 10
  64. CMD_TOP_LEFT = 11
  65. CMD_BOT_LEFT = 12
  66. CMD_PRPGRPH = 13
  67. CMD_NXPGRPH = 14
  68. CMD_PRPROMPT = 15
  69. CMD_NXPROMPT = 16
  70. CMD_PRSEARCH = 17
  71. CMD_NXSEARCH = 18
  72. CMD_CHRLT = 19
  73. CMD_CHRRT = 20
  74. CMD_HWINLT = 21
  75. CMD_HWINRT = 22
  76. CMD_FWINLT = 23
  77. CMD_FWINRT = 24
  78. CMD_FWINLTSKIP = 25
  79. CMD_FWINRTSKIP = 26
  80. CMD_LNBEG = 27
  81. CMD_LNEND = 28
  82. CMD_HOME = 29
  83. CMD_BACK = 30
  84. CMD_FREEZE = 31
  85. CMD_DISPMD = 32
  86. CMD_SIXDOTS = 33
  87. CMD_SLIDEWIN = 34
  88. CMD_SKPIDLNS = 35
  89. CMD_SKPBLNKWINS = 36
  90. CMD_CSRVIS = 37
  91. CMD_CSRHIDE = 38
  92. CMD_CSRTRK = 39
  93. CMD_CSRSIZE = 40
  94. CMD_CSRBLINK = 41
  95. CMD_ATTRVIS = 42
  96. CMD_ATTRBLINK = 43
  97. CMD_CAPBLINK = 44
  98. CMD_TUNES = 45
  99. CMD_HELP = 46
  100. CMD_INFO = 47
  101. CMD_LEARN = 48
  102. CMD_PREFMENU = 49
  103. CMD_PREFSAVE = 50
  104. CMD_PREFLOAD = 51
  105. CMD_MENU_FIRST_ITEM = 52
  106. CMD_MENU_LAST_ITEM = 53
  107. CMD_MENU_PREV_ITEM = 54
  108. CMD_MENU_NEXT_ITEM = 55
  109. CMD_MENU_PREV_SETTING = 56
  110. CMD_MENU_NEXT_SETTING = 57
  111. CMD_SAY_LINE = 58
  112. CMD_SAY_ABOVE = 59
  113. CMD_SAY_BELOW = 60
  114. CMD_MUTE = 61
  115. CMD_SPKHOME = 62
  116. CMD_SWITCHVT_PREV = 63
  117. CMD_SWITCHVT_NEXT = 64
  118. CMD_CSRJMP_VERT = 65
  119. CMD_PASTE = 66
  120. CMD_RESTARTBRL = 67
  121. CMD_RESTARTSPEECH = 68
  122. CMD_MAX = 68
  123. BRL_FLG_REPEAT_INITIAL = 8388608
  124. BRL_FLG_REPEAT_DELAY = 4194304
  125. command_name = { }
  126. command_name[CMD_FWINLT] = _('Line Left')
  127. command_name[CMD_FWINRT] = _('Line Right')
  128. command_name[CMD_LNUP] = _('Line Up')
  129. command_name[CMD_LNDN] = _('Line Down')
  130. command_name[CMD_FREEZE] = _('Freeze')
  131. command_name[CMD_TOP_LEFT] = _('Top Left')
  132. command_name[CMD_BOT_LEFT] = _('Bottom Right')
  133. command_name[CMD_HOME] = _('Cursor Position')
  134. _displaySize = [
  135.     32,
  136.     1]
  137. _lines = []
  138. _regionWithFocus = None
  139. viewport = [
  140.     0,
  141.     0]
  142. _callback = None
  143. endIsShowing = False
  144. beginningIsShowing = False
  145. cursorCell = 0
  146.  
  147. def _printBrailleEvent(level, command):
  148.     '''Prints out a Braille event.  The given level may be overridden
  149.     if the eventDebugLevel (see debug.setEventDebugLevel) is greater in
  150.     debug.py.
  151.  
  152.     Arguments:
  153.     - command: the BrlAPI command for the key that was pressed.
  154.     '''
  155.     debug.printInputEvent(level, 'BRAILLE EVENT: %x' % command)
  156.  
  157.  
  158. class Region:
  159.     '''A Braille region to be displayed on the display.  The width of
  160.     each region is determined by its string.
  161.     '''
  162.     
  163.     def __init__(self, string, cursorOffset = 0, expandOnCursor = False):
  164.         '''Creates a new Region containing the given string.
  165.  
  166.         Arguments:
  167.         - string: the string to be displayed
  168.         - cursorOffset: a 0-based index saying where to draw the cursor
  169.                         for this Region if it gets focus.
  170.         '''
  171.         if not string:
  172.             string = ''
  173.         
  174.         if settings.enableContractedBraille:
  175.             pass
  176.         self.contracted = louis is not None
  177.         self.expandOnCursor = expandOnCursor
  178.         self.rawLine = string.decode('UTF-8').strip('\n')
  179.         if self.contracted:
  180.             if not settings.brailleContractionTable:
  181.                 pass
  182.             self.contractionTable = _defaultContractionTable
  183.             (self.string, self.inPos, self.outPos, self.cursorOffset) = self.contractLine(self.rawLine, cursorOffset, expandOnCursor)
  184.         else:
  185.             self.string = self.rawLine
  186.             self.cursorOffset = cursorOffset
  187.  
  188.     
  189.     def processRoutingKey(self, offset):
  190.         '''Processes a cursor routing key press on this Component.  The offset
  191.         is 0-based, where 0 represents the leftmost character of string
  192.         associated with this region.  Note that the zeroeth character may have
  193.         been scrolled off the display.'''
  194.         pass
  195.  
  196.     
  197.     def getAttributeMask(self, getLinkMask = True):
  198.         """Creates a string which can be used as the attrOr field of brltty's
  199.         write structure for the purpose of indicating text attributes, links,
  200.         and selection.
  201.  
  202.         Arguments:
  203.         - getLinkMask: Whether or not we should take the time to get
  204.           the attributeMask for links. Reasons we might not want to
  205.           include knowning that we will fail and/or it taking an
  206.           unreasonable amount of time (AKA Gecko).
  207.         """
  208.         maskSize = len(self.string) + 2 * self.string.count(u'‚Ķ')
  209.         mask = [
  210.             '\x00'] * maskSize
  211.         return ''.join(mask)
  212.  
  213.     
  214.     def repositionCursor(self):
  215.         '''Reposition the cursor offset for contracted mode.
  216.         '''
  217.         if self.contracted:
  218.             (self.string, self.inPos, self.outPos, self.cursorOffset) = self.contractLine(self.rawLine, self.cursorOffset, self.expandOnCursor)
  219.         
  220.  
  221.     
  222.     def contractLine(self, line, cursorOffset = 0, expandOnCursor = False):
  223.         '''Contract the given line. Returns the contracted line, and the
  224.         cursor position in the contracted line.
  225.  
  226.         Arguments:
  227.         - line: Line to contract.
  228.         - cursorOffset: Offset of cursor,defaults to 0.
  229.         - expandOnCursor: Expand word under cursor, False by default.
  230.         '''
  231.         
  232.         try:
  233.             cursorOnSpace = line[cursorOffset] == ' '
  234.         except IndexError:
  235.             cursorOnSpace = False
  236.  
  237.         if not expandOnCursor or cursorOnSpace:
  238.             (contracted, inPos, outPos, cursorPos) = louis.translate([
  239.                 self.contractionTable], line, cursorPos = cursorOffset)
  240.         else:
  241.             (contracted, inPos, outPos, cursorPos) = louis.translate([
  242.                 self.contractionTable], line, cursorPos = cursorOffset, mode = louis.MODE.compbrlAtCursor)
  243.         return (contracted, inPos, outPos, cursorPos)
  244.  
  245.     
  246.     def displayToBufferOffset(self, display_offset):
  247.         
  248.         try:
  249.             offset = self.inPos[display_offset]
  250.         except IndexError:
  251.             offset = len(self.rawLine)
  252.         except AttributeError:
  253.             offset = display_offset
  254.  
  255.         return offset
  256.  
  257.     
  258.     def setContractedBraille(self, contracted):
  259.         if self.contracted == contracted:
  260.             return None
  261.         self.contracted = contracted
  262.         if contracted:
  263.             if not settings.brailleContractionTable:
  264.                 pass
  265.             self.contractionTable = _defaultContractionTable
  266.             self.contractRegion()
  267.         else:
  268.             self.expandRegion()
  269.  
  270.     
  271.     def contractRegion(self):
  272.         (self.string, self.inPos, self.outPos, self.cursorOffset) = self.contractLine(self.rawLine, self.cursorOffset, self.expandOnCursor)
  273.  
  274.     
  275.     def expandRegion(self):
  276.         if not self.contracted:
  277.             return None
  278.         self.string = self.rawLine
  279.         
  280.         try:
  281.             self.cursorOffset = self.inPos[self.cursorOffset]
  282.         except IndexError:
  283.             self.contracted
  284.             self.contracted
  285.             self.cursorOffset = len(self.string)
  286.         except:
  287.             self.contracted
  288.  
  289.  
  290.  
  291.  
  292. class Component(Region):
  293.     '''A subclass of Region backed by an accessible.  This Region will react
  294.     to any cursor routing key events and perform the default action on the
  295.     accessible, if a default action exists.
  296.     '''
  297.     
  298.     def __init__(self, accessible, string, cursorOffset = 0, indicator = '', expandOnCursor = False):
  299.         '''Creates a new Component.
  300.  
  301.         Arguments:
  302.         - accessible: the accessible
  303.         - string: the string to use to represent the component
  304.         - cursorOffset: a 0-based index saying where to draw the cursor
  305.                         for this Region if it gets focus.
  306.         '''
  307.         Region.__init__(self, string, cursorOffset, expandOnCursor)
  308.         if indicator:
  309.             if self.string:
  310.                 self.string = indicator + ' ' + self.string
  311.             else:
  312.                 self.string = indicator
  313.         
  314.         self.accessible = accessible
  315.  
  316.     
  317.     def processRoutingKey(self, offset):
  318.         '''Processes a cursor routing key press on this Component.  The offset
  319.         is 0-based, where 0 represents the leftmost character of string
  320.         associated with this region.  Note that the zeroeth character may have
  321.         been scrolled off the display.'''
  322.         
  323.         try:
  324.             action = self.accessible.queryAction()
  325.         except:
  326.             debug.println(debug.LEVEL_FINEST, 'braille.Component.processRoutingKey: no action')
  327.             
  328.             try:
  329.                 eventsynthesizer.clickObject(self.accessible, 1)
  330.             debug.printException(debug.LEVEL_SEVERE)
  331.  
  332.  
  333.         action.doAction(0)
  334.  
  335.  
  336.  
  337. class Link(Component):
  338.     """A subclass of Component backed by an accessible.  This Region will be
  339.     marked as a link by dots 7 or 8, depending on the user's preferences.
  340.     """
  341.     
  342.     def __init__(self, accessible, string, cursorOffset = 0):
  343.         '''Initialize a Link region. similar to Component, but here we always
  344.         have the region expand on cursor.'''
  345.         Component.__init__(self, accessible, string, cursorOffset, '', True)
  346.  
  347.     
  348.     def getAttributeMask(self, getLinkMask = True):
  349.         """Creates a string which can be used as the attrOr field of brltty's
  350.         write structure for the purpose of indicating text attributes and
  351.         selection.
  352.         Arguments:
  353.  
  354.         - getLinkMask: Whether or not we should take the time to get
  355.           the attributeMask for links. Reasons we might not want to
  356.           include knowning that we will fail and/or it taking an
  357.           unreasonable amount of time (AKA Gecko).
  358.         """
  359.         return chr(settings.brailleLinkIndicator) * len(self.string)
  360.  
  361.  
  362.  
  363. class Text(Region):
  364.     '''A subclass of Region backed by a Text object.  This Region will
  365.     react to any cursor routing key events by positioning the caret in
  366.     the associated text object. The line displayed will be the
  367.     contents of the text object preceded by an optional label.
  368.     [[[TODO: WDW - need to add in text selection capabilities.  Logged
  369.     as bugzilla bug 319754.]]]'''
  370.     
  371.     def __init__(self, accessible, label = '', eol = '', startOffset = None, endOffset = None):
  372.         '''Creates a new Text region.
  373.  
  374.         Arguments:
  375.         - accessible: the accessible that implements AccessibleText
  376.         - label: an optional label to display
  377.         '''
  378.         self.accessible = accessible
  379.         if orca_state.activeScript:
  380.             (string, self.caretOffset, self.lineOffset) = orca_state.activeScript.getTextLineAtCaret(self.accessible, startOffset)
  381.         else:
  382.             string = ''
  383.         string = string.decode('UTF-8')
  384.         if label:
  385.             label = label.decode('UTF-8')
  386.         
  387.         if eol:
  388.             eol = eol.decode('UTF-8')
  389.         
  390.         
  391.         try:
  392.             endOffset = endOffset - self.lineOffset
  393.         except TypeError:
  394.             pass
  395.  
  396.         
  397.         try:
  398.             self.startOffset = startOffset - self.lineOffset
  399.         except TypeError:
  400.             self.startOffset = 0
  401.  
  402.         string = string[self.startOffset:endOffset]
  403.         self.caretOffset -= self.startOffset
  404.         cursorOffset = min(self.caretOffset - self.lineOffset, len(string))
  405.         self._maxCaretOffset = self.lineOffset + len(string)
  406.         self.eol = eol
  407.         if label:
  408.             self.label = label + ' '
  409.         else:
  410.             self.label = ''
  411.         string = self.label + string
  412.         cursorOffset += len(self.label)
  413.         Region.__init__(self, string, cursorOffset, True)
  414.         if not (self.contracted) and not (settings.disableBrailleEOL):
  415.             self.string += self.eol
  416.         
  417.  
  418.     
  419.     def repositionCursor(self):
  420.         '''Attempts to reposition the cursor in response to a new
  421.         caret position.  If it is possible (i.e., the caret is on
  422.         the same line as it was), reposition the cursor and return
  423.         True.  Otherwise, return False.
  424.         '''
  425.         (string, caretOffset, lineOffset) = orca_state.activeScript.getTextLineAtCaret(self.accessible, self.startOffset)
  426.         string = string.decode('UTF-8')
  427.         cursorOffset = min(caretOffset - lineOffset, len(string))
  428.         if lineOffset != self.lineOffset:
  429.             return False
  430.         self.caretOffset = caretOffset
  431.         self.lineOffset = lineOffset
  432.         cursorOffset += len(self.label)
  433.         if self.contracted:
  434.             (self.string, self.inPos, self.outPos, cursorOffset) = self.contractLine(self.rawLine, cursorOffset, True)
  435.         
  436.         self.cursorOffset = cursorOffset
  437.         return True
  438.  
  439.     
  440.     def processRoutingKey(self, offset):
  441.         '''Processes a cursor routing key press on this Component.  The offset
  442.         is 0-based, where 0 represents the leftmost character of text
  443.         associated with this region.  Note that the zeroeth character may have
  444.         been scrolled off the display.'''
  445.         offset = self.displayToBufferOffset(offset)
  446.         if offset < 0:
  447.             return None
  448.         newCaretOffset = min(self.lineOffset + offset, self._maxCaretOffset)
  449.         orca_state.activeScript.setCaretOffset(self.accessible, newCaretOffset)
  450.  
  451.     
  452.     def getAttributeMask(self, getLinkMask = True):
  453.         """Creates a string which can be used as the attrOr field of brltty's
  454.         write structure for the purpose of indicating text attributes, links,
  455.         and selection.
  456.  
  457.         Arguments:
  458.         - getLinkMask: Whether or not we should take the time to get
  459.           the attributeMask for links. Reasons we might not want to
  460.           include knowning that we will fail and/or it taking an
  461.           unreasonable amount of time (AKA Gecko).
  462.         """
  463.         
  464.         try:
  465.             text = self.accessible.queryText()
  466.         except NotImplementedError:
  467.             return ''
  468.  
  469.         stringLength = len(self.rawLine) - len(self.label)
  470.         lineEndOffset = self.lineOffset + stringLength
  471.         regionMask = [
  472.             settings.TEXT_ATTR_BRAILLE_NONE] * stringLength
  473.         attrIndicator = settings.textAttributesBrailleIndicator
  474.         selIndicator = settings.brailleSelectorIndicator
  475.         linkIndicator = settings.brailleLinkIndicator
  476.         script = orca_state.activeScript
  477.         if getLinkMask and linkIndicator != settings.BRAILLE_LINK_NONE:
  478.             
  479.             try:
  480.                 hyperText = self.accessible.queryHypertext()
  481.                 nLinks = hyperText.getNLinks()
  482.             except:
  483.                 nLinks = 0
  484.  
  485.             n = 0
  486.             while n < nLinks:
  487.                 link = hyperText.getLink(n)
  488.                 if self.lineOffset <= link.startIndex:
  489.                     for i in xrange(link.startIndex, link.endIndex):
  490.                         
  491.                         try:
  492.                             regionMask[i] |= linkIndicator
  493.                         continue
  494.                         continue
  495.  
  496.                     
  497.                 
  498.                 n += 1
  499.         
  500.         if attrIndicator:
  501.             enabledAttributes = script.attribsToDictionary(settings.enabledBrailledTextAttributes)
  502.             offset = self.lineOffset
  503.             while offset < lineEndOffset:
  504.                 (attributes, startOffset, endOffset) = script.getTextAttributes(self.accessible, offset, True)
  505.                 if endOffset <= offset:
  506.                     break
  507.                 
  508.                 mask = settings.TEXT_ATTR_BRAILLE_NONE
  509.                 offset = endOffset
  510.                 for attrib in attributes:
  511.                     if enabledAttributes.get(attrib, '') != '':
  512.                         if enabledAttributes[attrib] != attributes[attrib]:
  513.                             mask = attrIndicator
  514.                             break
  515.                         
  516.                     enabledAttributes[attrib] != attributes[attrib]
  517.                 
  518.                 if mask != settings.TEXT_ATTR_BRAILLE_NONE:
  519.                     maskStart = max(startOffset - self.lineOffset, 0)
  520.                     maskEnd = min(endOffset - self.lineOffset, stringLength)
  521.                     for i in xrange(maskStart, maskEnd):
  522.                         regionMask[i] |= attrIndicator
  523.                     
  524.         
  525.         if selIndicator:
  526.             selections = script.getTextSelections(self.accessible)
  527.             for startOffset, endOffset in selections:
  528.                 maskStart = max(startOffset - self.lineOffset, 0)
  529.                 maskEnd = min(endOffset - self.lineOffset, stringLength)
  530.                 for i in xrange(maskStart, maskEnd):
  531.                     regionMask[i] |= selIndicator
  532.                 
  533.             
  534.         
  535.         regionMask += [
  536.             0] * len(self.eol)
  537.         if self.label:
  538.             regionMask = [
  539.                 0] * len(self.label) + regionMask
  540.         
  541.         return ''.join(map(chr, regionMask))
  542.  
  543.     
  544.     def contractLine(self, line, cursorOffset = 0, expandOnCursor = True):
  545.         (contracted, inPos, outPos, cursorPos) = Region.contractLine(self, line, cursorOffset, expandOnCursor)
  546.         return (contracted + self.eol, inPos, outPos, cursorPos)
  547.  
  548.     
  549.     def displayToBufferOffset(self, display_offset):
  550.         offset = Region.displayToBufferOffset(self, display_offset)
  551.         offset += self.startOffset
  552.         offset -= len(self.label)
  553.         return offset
  554.  
  555.     
  556.     def setContractedBraille(self, contracted):
  557.         Region.setContractedBraille(self, contracted)
  558.         if not contracted:
  559.             self.string += self.eol
  560.         
  561.  
  562.  
  563.  
  564. class ReviewComponent(Component):
  565.     '''A subclass of Component that is to be used for flat review mode.'''
  566.     
  567.     def __init__(self, accessible, string, cursorOffset, zone):
  568.         '''Creates a new Component.
  569.  
  570.         Arguments:
  571.         - accessible: the accessible
  572.         - string: the string to use to represent the component
  573.         - cursorOffset: a 0-based index saying where to draw the cursor
  574.                         for this Region if it gets focus.
  575.         - zone: the flat review Zone associated with this component
  576.         '''
  577.         Component.__init__(self, accessible, string, cursorOffset, expandOnCursor = True)
  578.         self.zone = zone
  579.  
  580.  
  581.  
  582. class ReviewText(Region):
  583.     '''A subclass of Region backed by a Text object.  This Region will
  584.     does not react to the caret changes, but will react if one updates
  585.     the cursorPosition.  This class is meant to be used by flat review
  586.     mode to show the current character position.
  587.     '''
  588.     
  589.     def __init__(self, accessible, string, lineOffset, zone):
  590.         '''Creates a new Text region.
  591.  
  592.         Arguments:
  593.         - accessible: the accessible that implements AccessibleText
  594.         - string: the string to use to represent the component
  595.         - lineOffset: the character offset into where the text line starts
  596.         - zone: the flat review Zone associated with this component
  597.         '''
  598.         Region.__init__(self, string, expandOnCursor = True)
  599.         self.accessible = accessible
  600.         self.lineOffset = lineOffset
  601.         self.zone = zone
  602.  
  603.     
  604.     def processRoutingKey(self, offset):
  605.         '''Processes a cursor routing key press on this Component.  The offset
  606.         is 0-based, where 0 represents the leftmost character of text
  607.         associated with this region.  Note that the zeroeth character may have
  608.         been scrolled off the display.'''
  609.         offset = self.displayToBufferOffset(offset)
  610.         newCaretOffset = self.lineOffset + offset
  611.         orca_state.activeScript.setCaretOffset(self.accessible, newCaretOffset)
  612.  
  613.  
  614.  
  615. class Line:
  616.     '''A horizontal line on the display.  Each Line is composed of a sequential
  617.     set of Regions.
  618.     '''
  619.     
  620.     def __init__(self, region = None):
  621.         self.regions = []
  622.         self.string = ''
  623.         if region:
  624.             self.addRegion(region)
  625.         
  626.  
  627.     
  628.     def addRegion(self, region):
  629.         self.regions.append(region)
  630.  
  631.     
  632.     def addRegions(self, regions):
  633.         self.regions.extend(regions)
  634.  
  635.     
  636.     def getLineInfo(self, getLinkMask = True):
  637.         '''Computes the complete string for this line as well as a
  638.         0-based index where the focused region starts on this line.
  639.         If the region with focus is not on this line, then the index
  640.         will be -1.
  641.  
  642.         Arguments:
  643.         - getLinkMask: Whether or not we should take the time to get
  644.           the attributeMask for links. Reasons we might not want to
  645.           include knowning that we will fail and/or it taking an
  646.           unreasonable amount of time (AKA Gecko).
  647.  
  648.         Returns [string, offsetIndex, attributeMask]
  649.         '''
  650.         string = ''
  651.         focusOffset = -1
  652.         attributeMask = ''
  653.         for region in self.regions:
  654.             if region == _regionWithFocus:
  655.                 focusOffset = len(string)
  656.             
  657.             if region.string:
  658.                 string += region.string.replace(u'‚Ķ', '...')
  659.             
  660.             mask = region.getAttributeMask(getLinkMask)
  661.             attributeMask += mask
  662.         
  663.         return [
  664.             string,
  665.             focusOffset,
  666.             attributeMask]
  667.  
  668.     
  669.     def getRegionAtOffset(self, offset):
  670.         '''Finds the Region at the given 0-based offset in this line.
  671.  
  672.         Returns the [region, offsetinregion] where the region is
  673.         the region at the given offset, and offsetinregion is the
  674.         0-based offset from the beginning of the region, representing
  675.         where in the region the given offset is.'''
  676.         foundRegion = None
  677.         string = ''
  678.         pos = 0
  679.         for region in self.regions:
  680.             foundRegion = region
  681.             string = string + region.string
  682.             if len(string) > offset:
  683.                 break
  684.                 continue
  685.             pos = len(string)
  686.         
  687.         if offset >= len(string):
  688.             return [
  689.                 None,
  690.                 -1]
  691.         return [
  692.             foundRegion,
  693.             offset - pos]
  694.  
  695.     
  696.     def processRoutingKey(self, offset):
  697.         '''Processes a cursor routing key press on this Component.  The offset
  698.         is 0-based, where 0 represents the leftmost character of string
  699.         associated with this line.  Note that the zeroeth character may have
  700.         been scrolled off the display.'''
  701.         (region, regionOffset) = self.getRegionAtOffset(offset)
  702.         if region:
  703.             region.processRoutingKey(regionOffset)
  704.         
  705.  
  706.     
  707.     def setContractedBraille(self, contracted):
  708.         for region in self.regions:
  709.             region.setContractedBraille(contracted)
  710.         
  711.  
  712.  
  713.  
  714. def getRegionAtCell(cell):
  715.     """Given a 1-based cell offset, return the braille region
  716.     associated with that cell in the form of [region, offsetinregion]
  717.     where 'region' is the region associated with the cell and
  718.     'offsetinregion' is the 0-based offset of where the cell is
  719.     in the region, where 0 represents the beginning of the region, """
  720.     if len(_lines) > 0:
  721.         offset = (cell - 1) + viewport[0]
  722.         lineNum = viewport[1]
  723.         return _lines[lineNum].getRegionAtOffset(offset)
  724.     return [
  725.         None,
  726.         -1]
  727.  
  728.  
  729. def clear():
  730.     '''Clears the logical structure, but keeps the Braille display as is
  731.     (until a refresh operation).
  732.     '''
  733.     global _lines, _regionWithFocus, viewport
  734.     _lines = []
  735.     _regionWithFocus = None
  736.     viewport = [
  737.         0,
  738.         0]
  739.  
  740.  
  741. def setLines(lines):
  742.     global _lines
  743.     _lines = lines
  744.  
  745.  
  746. def addLine(line):
  747.     '''Adds a line to the logical display for painting.  The line is added to
  748.     the end of the current list of known lines.  It is necessary for the
  749.     viewport to be over the lines and for refresh to be called for the new
  750.     line to be painted.
  751.  
  752.     Arguments:
  753.     - line: an instance of Line to add.
  754.     '''
  755.     _lines.append(line)
  756.     line._index = len(_lines)
  757.  
  758.  
  759. def getShowingLine():
  760.     '''Returns the Line that is currently being painted on the display.
  761.     '''
  762.     return _lines[viewport[1]]
  763.  
  764.  
  765. def setFocus(region, panToFocus = True, getLinkMask = True):
  766.     '''Specififes the region with focus.  This region will be positioned
  767.     at the home position if panToFocus is True.
  768.  
  769.     Arguments:
  770.     - region: the given region, which much be in a line that has been
  771.       added to the logical display
  772.     - panToFocus: whether or not to position the region at the home
  773.       position
  774.     - getLinkMask: Whether or not we should take the time to get the
  775.       attributeMask for links. Reasons we might not want to include
  776.       knowning that we will fail and/or it taking an unreasonable
  777.       amount of time (AKA Gecko).
  778.     '''
  779.     global _regionWithFocus
  780.     _regionWithFocus = region
  781.     if not panToFocus or not _regionWithFocus:
  782.         return None
  783.     lineNum = 0
  784.     done = False
  785.     for line in _lines:
  786.         for reg in line.regions:
  787.             if reg == _regionWithFocus:
  788.                 viewport[1] = lineNum
  789.                 done = True
  790.                 break
  791.                 continue
  792.             not _regionWithFocus
  793.         
  794.         if done:
  795.             break
  796.             continue
  797.         lineNum += 1
  798.     
  799.     line = _lines[viewport[1]]
  800.     (string, offset, attributeMask) = line.getLineInfo(getLinkMask)
  801.     if _regionWithFocus.cursorOffset >= _displaySize[0]:
  802.         offset += (_regionWithFocus.cursorOffset - _displaySize[0]) + 1
  803.     
  804.     viewport[0] = max(0, offset)
  805.  
  806.  
  807. def refresh(panToCursor = True, targetCursorCell = 0, getLinkMask = True):
  808.     """Repaints the Braille on the physical display.  This clips the entire
  809.     logical structure by the viewport and also sets the cursor to the
  810.     appropriate location.  [[[TODO: WDW - I'm not sure how BrlTTY handles
  811.     drawing to displays with more than one line, so I'm only going to handle
  812.     drawing one line right now.]]]
  813.  
  814.     Arguments:
  815.  
  816.     - panToCursor: if True, will adjust the viewport so the cursor is
  817.       showing.
  818.     - targetCursorCell: Only effective if panToCursor is True.
  819.       0 means automatically place the cursor somewhere on the display so
  820.       as to minimize movement but show as much of the line as possible.
  821.       A positive value is a 1-based target cell from the left side of
  822.       the display and a negative value is a 1-based target cell from the
  823.       right side of the display.
  824.     - getLinkMask: Whether or not we should take the time to get the
  825.       attributeMask for links. Reasons we might not want to include
  826.       knowning that we will fail and/or it taking an unreasonable
  827.       amount of time (AKA Gecko).
  828.     """
  829.     global cursorCell, cursorCell, cursorCell, monitor, monitor, beginningIsShowing, endIsShowing
  830.     if len(_lines) == 0:
  831.         if useBrlAPIBindings:
  832.             if brlAPIRunning:
  833.                 brlAPI.writeText('', 0)
  834.             
  835.         else:
  836.             brl.writeText(0, '')
  837.         return None
  838.     if targetCursorCell < 0:
  839.         targetCursorCell = _displaySize[0] + targetCursorCell + 1
  840.     
  841.     line = _lines[viewport[1]]
  842.     (string, focusOffset, attributeMask) = line.getLineInfo(getLinkMask)
  843.     cursorOffset = -1
  844.     if focusOffset >= 0:
  845.         cursorOffset = focusOffset + _regionWithFocus.cursorOffset
  846.     
  847.     if panToCursor and cursorOffset >= 0:
  848.         if len(string) <= _displaySize[0]:
  849.             viewport[0] = 0
  850.         elif targetCursorCell:
  851.             viewport[0] = max(0, (cursorOffset - targetCursorCell) + 1)
  852.         elif cursorOffset < viewport[0]:
  853.             viewport[0] = max(0, cursorOffset)
  854.         elif cursorOffset >= viewport[0] + _displaySize[0]:
  855.             viewport[0] = max(0, (cursorOffset - _displaySize[0]) + 1)
  856.         
  857.     
  858.     startPos = viewport[0]
  859.     endPos = startPos + _displaySize[0]
  860.     cursorCell = cursorOffset - startPos
  861.     if cursorCell < 0 or cursorCell >= _displaySize[0]:
  862.         cursorCell = 0
  863.     else:
  864.         cursorCell += 1
  865.     logLine = "BRAILLE LINE:  '%s'" % string
  866.     debug.println(debug.LEVEL_INFO, logLine)
  867.     log.info(logLine.encode('UTF-8'))
  868.     logLine = "     VISIBLE:  '%s', cursor=%d" % (string[startPos:endPos], cursorCell)
  869.     debug.println(debug.LEVEL_INFO, logLine)
  870.     log.info(logLine.encode('UTF-8'))
  871.     substring = string[startPos:endPos]
  872.     if useBrlAPIBindings:
  873.         if brlAPIRunning:
  874.             writeStruct = brlapi.WriteStruct()
  875.             writeStruct.regionBegin = 1
  876.             writeStruct.regionSize = len(substring)
  877.             while writeStruct.regionSize < _displaySize[0]:
  878.                 substring += ' '
  879.                 if attributeMask:
  880.                     attributeMask += '\x00'
  881.                 
  882.                 writeStruct.regionSize += 1
  883.                 continue
  884.                 writeStruct
  885.             writeStruct.text = substring
  886.             writeStruct.cursor = cursorCell
  887.             if attributeMask:
  888.                 writeStruct.attrOr = attributeMask[startPos:endPos]
  889.             
  890.             brlAPI.write(writeStruct)
  891.         
  892.     else:
  893.         brl.writeText(cursorCell, substring)
  894.     if settings.enableBrailleMonitor:
  895.         if not monitor:
  896.             monitor = brlmon.BrlMon(_displaySize[0])
  897.             monitor.show_all()
  898.         
  899.         if attributeMask:
  900.             subMask = attributeMask[startPos:endPos]
  901.         else:
  902.             subMask = None
  903.         monitor.writeText(cursorCell, substring, subMask)
  904.     elif monitor:
  905.         monitor.destroy()
  906.         monitor = None
  907.     
  908.     beginningIsShowing = startPos == 0
  909.     endIsShowing = endPos >= len(string)
  910.  
  911.  
  912. def displayRegions(regionInfo):
  913.     '''Displays a list of regions on a single line, setting focus to the
  914.        specified region.  The regionInfo parameter is something that is
  915.        typically returned by a call to braillegenerator.getBrailleRegions.
  916.  
  917.     Arguments:
  918.     - regionInfo: a list where the first element is a list of regions
  919.                   to display and the second element is the region
  920.                   with focus (must be in the list from element 0)
  921.     '''
  922.     regions = regionInfo[0]
  923.     focusedRegion = regionInfo[1]
  924.     clear()
  925.     line = Line()
  926.     for item in regions:
  927.         line.addRegion(item)
  928.     
  929.     addLine(line)
  930.     setFocus(focusedRegion)
  931.     refresh()
  932.  
  933.  
  934. def displayMessage(message, cursor = -1):
  935.     '''Displays a single line, setting the cursor to the given position,
  936.     ensuring that the cursor is in view.
  937.  
  938.     Arguments:
  939.     - message: the string to display
  940.     - cursor: the 0-based cursor position, where -1 (default) means no cursor
  941.     '''
  942.     clear()
  943.     region = Region(message, cursor)
  944.     addLine(Line(region))
  945.     setFocus(region)
  946.     refresh(True)
  947.  
  948.  
  949. def panLeft(panAmount = 0):
  950.     '''Pans the display to the left, limiting the pan to the beginning
  951.     of the line being displayed.
  952.  
  953.     Arguments:
  954.     - panAmount: the amount to pan.  A value of 0 means the entire
  955.                  width of the physical display.
  956.  
  957.     Returns True if a pan actually happened.
  958.     '''
  959.     oldX = viewport[0]
  960.     if panAmount == 0:
  961.         panAmount = _displaySize[0]
  962.     
  963.     if viewport[0] > 0:
  964.         viewport[0] = max(0, viewport[0] - panAmount)
  965.     
  966.     return oldX != viewport[0]
  967.  
  968.  
  969. def panRight(panAmount = 0):
  970.     '''Pans the display to the right, limiting the pan to the length
  971.     of the line being displayed.
  972.  
  973.     Arguments:
  974.     - panAmount: the amount to pan.  A value of 0 means the entire
  975.                  width of the physical display.
  976.  
  977.     Returns True if a pan actually happened.
  978.     '''
  979.     oldX = viewport[0]
  980.     if panAmount == 0:
  981.         panAmount = _displaySize[0]
  982.     
  983.     if len(_lines) > 0:
  984.         lineNum = viewport[1]
  985.         newX = viewport[0] + panAmount
  986.         (string, focusOffset, attributeMask) = _lines[lineNum].getLineInfo()
  987.         if newX < len(string):
  988.             viewport[0] = newX
  989.         
  990.     
  991.     return oldX != viewport[0]
  992.  
  993.  
  994. def panToOffset(offset):
  995.     '''Automatically pan left or right to make sure the current offset is
  996.     showing.'''
  997.     while offset < viewport[0]:
  998.         debug.println(debug.LEVEL_FINEST, 'braille.panToOffset (left) %d' % offset)
  999.         if not panLeft():
  1000.             break
  1001.             continue
  1002.     while offset >= viewport[0] + _displaySize[0]:
  1003.         debug.println(debug.LEVEL_FINEST, 'braille.panToOffset (right) %d' % offset)
  1004.         if not panRight():
  1005.             break
  1006.             continue
  1007.  
  1008.  
  1009. def returnToRegionWithFocus(inputEvent = None):
  1010.     '''Pans the display so the region with focus is displayed.
  1011.  
  1012.     Arguments:
  1013.     - inputEvent: the InputEvent instance that caused this to be called.
  1014.  
  1015.     Returns True to mean the command should be consumed.
  1016.     '''
  1017.     setFocus(_regionWithFocus)
  1018.     refresh(True)
  1019.     return True
  1020.  
  1021.  
  1022. def _processBrailleEvent(command):
  1023.     '''Handles BrlTTY command events.  This passes commands on to Orca for
  1024.     processing.  If Orca does not handle them (as indicated by a return value
  1025.     of false from the callback passed to init, it will attempt to handle the
  1026.     command itself - either by panning the viewport or passing cursor routing
  1027.     keys to the Regions for handling.
  1028.  
  1029.     Arguments:
  1030.     - command: the BrlAPI command for the key that was pressed.
  1031.     '''
  1032.     _printBrailleEvent(debug.LEVEL_FINE, command)
  1033.     if command & BRL_FLG_REPEAT_INITIAL:
  1034.         command &= ~(BRL_FLG_REPEAT_INITIAL | BRL_FLG_REPEAT_DELAY)
  1035.     elif command & BRL_FLG_REPEAT_DELAY:
  1036.         return True
  1037.     consumed = False
  1038.     if settings.timeoutCallback and settings.timeoutTime > 0:
  1039.         signal.signal(signal.SIGALRM, settings.timeoutCallback)
  1040.         signal.alarm(settings.timeoutTime)
  1041.     
  1042.     if _callback:
  1043.         
  1044.         try:
  1045.             consumed = _callback(command)
  1046.         debug.printException(debug.LEVEL_WARNING)
  1047.         consumed = False
  1048.  
  1049.     
  1050.     if command >= 256 and command < 256 + _displaySize[0]:
  1051.         if len(_lines) > 0:
  1052.             cursor = (command - 256) + viewport[0]
  1053.             lineNum = viewport[1]
  1054.             _lines[lineNum].processRoutingKey(cursor)
  1055.             consumed = True
  1056.         
  1057.     
  1058.     if command in (8513, 65):
  1059.         settings.enableContractedBraille = not (settings.enableContractedBraille)
  1060.         for line in _lines:
  1061.             line.setContractedBraille(settings.enableContractedBraille)
  1062.         
  1063.         refresh()
  1064.     
  1065.     if settings.timeoutCallback and settings.timeoutTime > 0:
  1066.         signal.alarm(0)
  1067.     
  1068.     return consumed
  1069.  
  1070.  
  1071. def _brlAPIKeyReader(source, condition):
  1072.     '''Method to read a key from the BrlAPI bindings.  This is a
  1073.     gobject IO watch handler.
  1074.     '''
  1075.     key = brlAPI.readKey(False)
  1076.     if key:
  1077.         lower = key & 0xFFFFFFFFL
  1078.         keyCode = lower & 536870911
  1079.         if keyCode & 65536:
  1080.             keyCode = 256 | keyCode & 255
  1081.         
  1082.         if keyCode:
  1083.             _processBrailleEvent(keyCode)
  1084.         
  1085.     
  1086.     return brlAPIRunning
  1087.  
  1088.  
  1089. def setupKeyRanges(keys):
  1090.     '''Hacky method to tell BrlTTY what to send and not send us via
  1091.     the readKey method.  This only works with BrlTTY v3.8 and better.
  1092.  
  1093.     Arguments:
  1094.     -keys: a list of BrlAPI commands.
  1095.     '''
  1096.     if not brlAPIRunning:
  1097.         return None
  1098.     
  1099.     try:
  1100.         brlAPI.ignoreKeys(brlapi.rangeType_all, [
  1101.             0])
  1102.         keySet = [
  1103.             brlapi.KEY_TYPE_CMD | brlapi.KEY_CMD_ROUTE]
  1104.         for key in keys:
  1105.             keySet.append(brlapi.KEY_TYPE_CMD | key)
  1106.         
  1107.         brlAPI.acceptKeys(brlapi.rangeType_command, keySet)
  1108.         brlAPI.acceptKeys(brlapi.rangeType_key, [
  1109.             65])
  1110.         debug.println(debug.LEVEL_FINEST, 'Using BrlAPI v0.5.0+')
  1111.     except:
  1112.         brlAPIRunning
  1113.         debug.printException(debug.LEVEL_FINEST)
  1114.         
  1115.         try:
  1116.             brlAPI.ignoreKeyRange(0, brlapi.KEY_FLAGS_MASK | brlapi.KEY_TYPE_MASK | brlapi.KEY_CODE_MASK)
  1117.             brlAPI.acceptKeyRange(brlapi.KEY_TYPE_CMD | brlapi.KEY_CMD_ROUTE, brlapi.KEY_TYPE_CMD | brlapi.KEY_CMD_ROUTE | brlapi.KEY_CMD_ARG_MASK)
  1118.             keySet = []
  1119.             for key in keys:
  1120.                 keySet.append(brlapi.KEY_TYPE_CMD | key)
  1121.             
  1122.             if len(keySet):
  1123.                 brlAPI.acceptKeySet(keySet)
  1124.             
  1125.             debug.println(debug.LEVEL_FINEST, 'Using BrlAPI pre-release v0.5.0')
  1126.         debug.printException(debug.LEVEL_FINEST)
  1127.         debug.println(debug.LEVEL_WARNING, 'Braille module cannot listen for braille input events')
  1128.  
  1129.  
  1130.  
  1131.  
  1132. def init(callback = None, tty = 7):
  1133.     '''Initializes the braille module, connecting to the BrlTTY driver.
  1134.  
  1135.     Arguments:
  1136.     - callback: the method to call with a BrlTTY input event.
  1137.     - tty: the tty port to take ownership of (default = 7)
  1138.     Returns True if the initialization procedure was run or False if this
  1139.     module has already been initialized.
  1140.     '''
  1141.     global _callback, brlAPI, brlAPIRunning, brlAPIRunning, brlAPISourceId, _displaySize, _displaySize, _initialized
  1142.     if _initialized:
  1143.         return False
  1144.     _callback = callback
  1145.     if useBrlAPIBindings:
  1146.         
  1147.         try:
  1148.             gobject.threads_init()
  1149.             brlAPI = brlapi.Connection()
  1150.             
  1151.             try:
  1152.                 import os
  1153.                 windowPath = os.environ['WINDOWPATH']
  1154.                 brlAPI.enterTtyModeWithPath()
  1155.                 brlAPIRunning = True
  1156.                 debug.println(debug.LEVEL_CONFIGURATION, 'Braille module has been initialized using WINDOWPATH=' + '%s' % windowPath)
  1157.             except:
  1158.                 _initialized
  1159.                 brlAPI.enterTtyMode(tty)
  1160.                 brlAPIRunning = True
  1161.                 debug.println(debug.LEVEL_CONFIGURATION, 'Braille module has been initialized using tty=%d' % tty)
  1162.  
  1163.             brlAPISourceId = gobject.io_add_watch(brlAPI.fileDescriptor, gobject.IO_IN, _brlAPIKeyReader)
  1164.         debug.printException(debug.LEVEL_FINEST)
  1165.         return False
  1166.  
  1167.     elif brl.init(tty):
  1168.         debug.println(debug.LEVEL_CONFIGURATION, 'Braille module has been initialized.')
  1169.         brl.registerCallback(_processBrailleEvent)
  1170.     else:
  1171.         debug.println(debug.LEVEL_CONFIGURATION, 'Braille module has NOT been initialized.')
  1172.         return False
  1173.     if _initialized:
  1174.         (x, y) = brlAPI.displaySize
  1175.         _displaySize = [
  1176.             x,
  1177.             1]
  1178.     else:
  1179.         _displaySize = [
  1180.             brl.getDisplayWidth(),
  1181.             1]
  1182.     debug.println(debug.LEVEL_CONFIGURATION, 'braille display size = (%d, %d)' % (_displaySize[0], _displaySize[1]))
  1183.     clear()
  1184.     refresh(True)
  1185.     _initialized = True
  1186.     return True
  1187.  
  1188.  
  1189. def shutdown():
  1190.     '''Shuts down the braille module.   Returns True if the shutdown procedure
  1191.     was run or False if this module has not been initialized.
  1192.     '''
  1193.     global brlAPIRunning, brlAPISourceId, _initialized
  1194.     if not _initialized:
  1195.         return False
  1196.     if useBrlAPIBindings:
  1197.         if brlAPIRunning:
  1198.             brlAPIRunning = False
  1199.             gobject.source_remove(brlAPISourceId)
  1200.             brlAPISourceId = 0
  1201.             brlAPI.leaveTtyMode()
  1202.         
  1203.     else:
  1204.         brl.shutdown()
  1205.     _initialized = False
  1206.     return True
  1207.  
  1208.